package org.codehaus.activemq.store.bdbn;

import com.sleepycat.bdb.CurrentTransaction;
import com.sleepycat.db.Db;
import com.sleepycat.db.DbEnv;
import com.sleepycat.db.DbException;
import com.sleepycat.db.DbTxn;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.LinkedList;
import javax.jms.JMSException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.activemq.util.JMSExceptionHelper;

public class BDbHelper
{
  private static final Log log = LogFactory.getLog(BDbHelper.class);
  private static ThreadLocal threadLocalTxn = new ThreadLocal();
  public static final int TRANSACTION_FLAGS = Db.DB_TXN_SYNC;
  private static DbEnv cachedEnvironment;

  public static DbEnv createEnvironment(File dir, boolean runRecovery)
    throws DbException, FileNotFoundException
  {
    DbEnv env = new DbEnv(0);

    int envFlags = Db.DB_INIT_TXN | Db.DB_INIT_LOCK | Db.DB_INIT_LOG | Db.DB_INIT_MPOOL | Db.DB_CREATE;

    if (runRecovery) {
      envFlags |= Db.DB_RECOVER;
    }
    env.open(dir.getPath(), envFlags, 0);
    return env;
  }

  public static Db open(DbEnv environment, String name, boolean isQueue) throws FileNotFoundException, DbException, JMSException {
    int flags = Db.DB_CREATE;
    Db db = new Db(environment, 0);

    if (isQueue) {
      db.setFlags(Db.DB_RENUMBER);
    }

    int type = 1;
    if (isQueue) {
      type = 3;
    }
    String databaseName = null;
    DbTxn transaction = createTransaction(environment);
    try {
      db.open(transaction, name, databaseName, type, flags, 0);
      transaction = commitTransaction(transaction);
    }
    finally {
      rollbackTransaction(transaction);
    }
    return db;
  }

  public static DbTxn getTransaction()
  {
    LinkedList list = (LinkedList)threadLocalTxn.get();
    if ((list != null) && (!list.isEmpty())) {
      return (DbTxn)list.getFirst();
    }
    return null;
  }

  public static DbTxn popTransaction()
  {
    LinkedList list = (LinkedList)threadLocalTxn.get();
    if ((list == null) || (list.isEmpty())) {
      log.warn("Attempt to pop transaction when no transaction in progress");
      return null;
    }

    return (DbTxn)list.removeFirst();
  }

  public static void pushTransaction(DbTxn transaction)
  {
    LinkedList list = (LinkedList)threadLocalTxn.get();
    if (list == null) {
      list = new LinkedList();
      threadLocalTxn.set(list);
    }
    list.addLast(transaction);
  }

  public static int getTransactionCount() {
    LinkedList list = (LinkedList)threadLocalTxn.get();
    if (list != null) {
      return list.size();
    }
    return 0;
  }

  public static DbTxn createTransaction(DbEnv environment)
    throws DbException
  {
    cachedEnvironment = environment;
    CurrentTransaction currentTxn = CurrentTransaction.getInstance(environment);
    return currentTxn.beginTxn();
  }

  public static DbTxn commitTransaction(DbTxn transaction)
    throws JMSException
  {
    try
    {
      CurrentTransaction currentTxn = CurrentTransaction.getInstance(cachedEnvironment);
      currentTxn.commitTxn();
      return null;
    } catch (DbException e) {
    	  throw JMSExceptionHelper.newJMSException("Failed to commit transaction: " + transaction + " in container: " + e, e);
    	  
    }
   }

  public static void rollbackTransaction(DbTxn transaction)
  {
    if (transaction != null)
      try {
        CurrentTransaction currentTxn = CurrentTransaction.getInstance(cachedEnvironment);
        currentTxn.abortTxn();
      }
      catch (DbException e) {
        log.warn("Cannot rollback transaction due to: " + e, e);
      }
  }
}